import gc
import json
import os
import sqlite3
import threading
import time
import matplotlib

matplotlib.use('agg')
import matplotlib.pyplot as plt
from fractions import Fraction
import re


def synchronized(func):
    func.__lock__ = threading.Lock()

    def lock_func(*args, **kwargs):
        with func.__lock__:
            return func(*args, **kwargs)

    return lock_func


class Singleton(type):
    _instances = {}

    # @synchronized
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class AC(object):
    """
    系统开机时建立，与系统生命周期一致
    前台退房时将模式设为停止, 并将计费清零
    """

    def __init__(self) -> None:
        self.target_temp: Fraction = Fraction()  # 由房客端传过来
        self.room_temp: Fraction = Fraction()  # 由房客端传过来
        self.fan_speed: int = 0  # 提交申请后由服务对象修改
        self.fee_sum: Fraction = Fraction()  # 由服务对象服务过程内修改
        self.mode: str = 'stop'  # 0-4: stop run wait pause
        self.mode_str: 'list[str]' = ['stop', 'run', 'wait', 'pause']
        self.data_lock = threading.Lock()  # 同步锁

    def get_mode(self):
        with self.data_lock:
            return self.mode

    def get_fee_sum(self):
        with self.data_lock:
            return self.fee_sum

    def get_fan_speed(self):
        with self.data_lock:
            return self.fan_speed

    def get_room_temp(self):
        with self.data_lock:
            return self.room_temp

    def get_target_temp(self):
        with self.data_lock:
            return self.target_temp

    def set_room_mode(self, mode: str):
        """
        modified by scheduler
        stop: power off
        pause: req_release
        :param mode: 0-4: stop run wait pause
        :return:
        """
        with self.data_lock:
            if mode in self.mode_str:
                self.mode = mode
                return True
            return False

    def set_target_temp(self, temp: float) -> None:
        with self.data_lock:
            self.target_temp = Fraction(temp)

    def set_room_temp(self, temp: float) -> None:
        """
        调用power off 时清零, 调用 poweron 时被设置
        """
        with self.data_lock:
            self.room_temp = Fraction(temp)

    def set_fan_speed(self, fan_speed: int) -> None:
        with self.data_lock:
            self.fan_speed = fan_speed

    def fee_increase(self, inc: Fraction) -> None:
        with self.data_lock:
            self.fee_sum += inc

    def set_fee(self, fee: float):
        with self.data_lock:
            self.fee_sum = Fraction(fee)


class CenturalAC(object, metaclass=Singleton):
    """
    PowerOn时创建类: Centural_AC, Server, Scheduler, Database
    StartUp时创建类：Centural_AC内的各实例, User_Facade(依赖于温度范围), Reception_facade(依赖于房间列表)
    RDManager(依赖roomlist)

    temp_change: 工作中空调每min温度改变量
    """

    def __init__(self) -> None:

        self.is_working = threading.Event()  # 系统是否正常工作 isSet() 检查状态, 开机Set 关机Clear
        # 此信号的状态为scheduler循环条件
        self.is_working.clear()
        self.cool_warm = False  # True制冷、False制热 cool时温度调节时不允许目标温度大于室温
        self.AC_list: 'dict[int, AC]' = {}
        self.def_temp: Fraction = Fraction()
        self.fan_speed: int = 0  # 1, 2, 3
        self.temp_lower: float = 0
        self.temp_upper: float = 0
        self.fee_rate: dict = {}
        self.room_list: list = []
        self.temp_change = [0, Fraction(1, 3), Fraction(1, 2), Fraction(1)]

    def set_para(self, cool_warm: bool, def_temp: float, def_fan_speed: int,
                 temp_lower: float, temp_upper: float, fee_rate_l: Fraction, fee_rate_m: Fraction,
                 fee_rate_h: Fraction, room_list: list) -> bool:
        """
        :param fee_rate_h:
        :param fee_rate_m:
        :param fee_rate_l:
        :param cool_warm: true 制冷 false 制热
        :param room_list:
        :param def_temp:
        :param def_fan_speed:
        :param temp_lower:
        :param temp_upper:
        :return:
        """
        if def_temp > temp_upper or def_temp < temp_lower:
            print('invalid def temp:', def_temp)
            return False
        elif def_fan_speed not in range(1, 4):
            print('invalid fan speed:', def_fan_speed)
            return False
        elif fee_rate_h < 0 or fee_rate_l < 0 or fee_rate_m < 0:
            print('invalid fee rate:', fee_rate_h, fee_rate_m, fee_rate_l)
            return False
        self.cool_warm: bool = cool_warm
        self.def_temp = Fraction(def_temp)
        self.fan_speed: int = def_fan_speed
        self.temp_lower: float = temp_lower
        self.temp_upper: float = temp_upper
        self.fee_rate: dict = {1: fee_rate_l, 2: fee_rate_m, 3: fee_rate_h}
        print(self.fee_rate)
        self.room_list: list = room_list
        return True

    def startup(self) -> None:
        self.AC_list = {room: AC() for room in self.room_list}
        print(self.fee_rate)
        self.is_working.set()
        UserFacade(self.temp_lower, self.temp_upper, self.room_list)
        ReceptionFacade(self.room_list)
        RDManager(self.room_list)
        print("start up successfully")

    def shutdown(self):
        self.is_working.clear()
        # self.is_open = False
        if not os.path.exists(r'.is_open'):
            raise Exception('No working flag file!')
        os.remove(r'.is_open')

    def reset_ac(self, room_id: int):
        """
        reset ac, 仅在前台处调用
        清空计费, 并关闭未关的空调
        """
        self.set_fee(room_id, 0)
        if self.get_mode(room_id) != 'stop':
            self.set_room_mode(room_id, 'stop')
            self.poweroff_ac(room_id, power_off=True)

    def poweron_ac(self, room_id: int):
        """
        Set ac parameters, submit request
        :param room_id:
        :return:
        """
        self.AC_list[room_id].set_target_temp(self.def_temp)
        thesch = Scheduler()
        thesch.submit_task(room_id, self.fan_speed)

    def poweroff_ac(self, room_id: int, power_off: bool):
        """
        :param room_id:房号
        :param power_off: True to power off, False 则 request_release
        :return:
        """
        # 已release，则队列内无对象
        # 未stop/release,则队列内有对象
        t_m = self.get_mode(room_id)
        thesch = Scheduler()
        if power_off:  # power off
            self.AC_list[room_id].set_room_mode('stop')
            self.AC_list[room_id].set_target_temp(0)
            thesch.end_task(room_id, True)
        else:
            if t_m == 'run' or t_m == 'wait':  # 在run 或 wait
                self.set_room_mode(room_id, 'pause')
                thesch.end_task(room_id, False)

    def get_surveillance_stat(self, room_id: int):
        """
            return
                冷暖 工作状态 目标温度 室内温度 风速 服务时长 本次费用 累计费用
                of the AC
        """
        fs = self.get_fan_speed(room_id)
        tt = self.get_target_temp(room_id)
        rt = self.get_room_temp(room_id)
        feesum = self.get_fee_sum(room_id)
        mode = self.AC_list[room_id].mode_str.index(self.get_mode(room_id))
        thesch = Scheduler()
        return int(self.cool_warm), mode, round(float(tt), 1), round(float(rt), 1), fs, \
               thesch.get_serv_duration(room_id), round(float(thesch.get_fee_sum(room_id)), 1), round(float(feesum), 1)

    def get_mode(self, room_id: int):
        return self.AC_list[room_id].get_mode()

    def get_fee_sum(self, room_id: int):
        return self.AC_list[room_id].get_fee_sum()

    def get_fan_speed(self, room_id: int):
        return self.AC_list[room_id].get_fan_speed()

    def get_room_temp(self, room_id: int):
        return self.AC_list[room_id].get_room_temp()

    def get_target_temp(self, room_id: int):
        return self.AC_list[room_id].get_target_temp()

    def set_room_mode(self, room_id: int, mode: str):
        """
        room mode: stop run wait pause
        """
        self.AC_list[room_id].set_room_mode(mode)

    def set_target_temp(self, room_id: int, temp: float) -> None:
        self.AC_list[room_id].set_target_temp(temp)

    def set_room_temp(self, room_id: int, temp: float) -> None:
        self.AC_list[room_id].set_room_temp(temp)

    def update_room_temp(self, room_id: int) -> bool:
        """
        called by service function
        return True if room temp hasn't reach target
        return False if room temp == target temp
        """
        room_temp = self.AC_list[room_id].get_room_temp()
        target_temp = self.AC_list[room_id].get_target_temp()
        if self.cool_warm:  # cool
            room_temp -= self.temp_change[self.AC_list[room_id].get_fan_speed()]
            self.AC_list[room_id].set_room_temp(room_temp)
            if room_temp <= target_temp:
                self.AC_list[room_id].set_room_temp(target_temp)
                return False
            return True
        else:
            room_temp += self.temp_change[self.AC_list[room_id].get_fan_speed()]
            self.AC_list[room_id].set_room_temp(room_temp)
            if room_temp >= target_temp:
                self.AC_list[room_id].set_room_temp(target_temp)
                return False
            return True

    def change_fan_speed(self, room_id: int, fan_speed: int) -> None:
        """
        被用户调用
        """
        thesch = Scheduler()
        print('change fs')
        thesch.submit_task(room_id, fan_speed)

    def set_fan_speed(self, room_id: int, fan_speed: int) -> None:
        """
        仅被服务对象直接调用
        """
        self.AC_list[room_id].set_fan_speed(fan_speed)

    def fee_increase(self, room_id: int, inc: Fraction) -> None:
        self.AC_list[room_id].fee_increase(inc)

    def set_fee(self, room_id: int, fee: float):
        self.AC_list[room_id].set_fee(fee)


class RD(object):
    """
    command = "insert into details (count, room_id, charge_fee, fan_speed, \
    submit_time, start_time, end_time, start_temp, end_temp, fee_sum) VALUES ("
    """

    def __init__(self, room_id: int, fee_rate: Fraction, fan_speed: int, submit_time: float,
                 s_time: float, e_time: float, s_temp: Fraction, e_temp: Fraction, fee_sum: Fraction):
        self.room_id: int = room_id  # 房号
        self.fan_speed: int = fan_speed  # 风速
        self.fee_rate: Fraction = fee_rate  # 费率
        self.fee_sum = fee_sum  # 本次服务费用
        self.submit_time: float = round(submit_time, 0)  # 服务请求时间
        self.s_time: float = round(s_time, 0)  # 服务开始时间
        self.e_time: float = round(e_time, 0)  # 服务结束时间
        self.s_temp: Fraction = s_temp  # 服务开始温度
        self.e_temp: Fraction = e_temp  # 服务结束温度

    def to_list(self):
        """
        将RD转换为字符串便于发送
        """
        ret_list = [self.room_id, self.fan_speed, float(self.fee_rate), float(self.fee_sum),
                    self.submit_time, self.s_time, self.e_time, float(self.s_temp), float(self.e_temp)]
        return ret_list

    def show(self):
        print('room id: {}\tfan speed: {}\tfee_rate: {}\tfee_sum: {}'
              .format(self.room_id, self.fan_speed, self.fee_rate, self.fee_sum))
        print('submit_time: {}\ts_time: {}\te_time: {}\ts_temp: {}\te_temp: {}'
              .format(self.submit_time, self.s_time, self.e_time, self.s_temp, self.e_temp))


class RDManager(object, metaclass=Singleton):

    def __init__(self, room_list: 'list[int]'):
        ac = CenturalAC()
        if not ac.is_working.is_set():
            print("Reception Facade should be created after the creation of Centural AC")
            exit(1)
        self.RD_list: 'dict[int, list[RD]]' = {room_id: [] for room_id in room_list}

    def create_rd(self, room_id: int, fan_speed: int, fee_rate: Fraction, fee_sum: Fraction,
                  submit_time: float, s_time: float, e_time: float, s_temp: Fraction, e_temp: Fraction):
        t_rd: RD = RD(
            room_id=room_id, fan_speed=fan_speed,
            fee_rate=fee_rate, fee_sum=fee_sum,
            submit_time=submit_time,
            s_time=s_time, e_time=e_time,
            s_temp=s_temp, e_temp=e_temp
        )
        self.RD_list[room_id].append(t_rd)

    def archive_rd(self, room_id: int):
        """
        将某房间详单从内存挪到数据库, 仅前台控制器退房时调用
        """
        the_db = DataBase()
        for rd in self.RD_list[room_id]:
            the_db.insert_detail(rd)
        self.RD_list[room_id] = []

    def get_mem_rd(self, room_id: int):
        """
        获取某房间的rd
        """
        return self.RD_list[room_id]

    def get_all_rd(self):
        ret = self.get_db_rd()
        for i in self.RD_list.keys():
            if len(self.RD_list[i]):
                ret += self.RD_list[i]
        return ret

    def get_db_rd(self):
        """
        获取数据库内所有RD
        """
        the_db = DataBase()
        return the_db.query_details()


class ServiceItem(object):

    def __init__(self, room_id: int, fan_speed: int):
        self.room_id = room_id
        self.fan_speed = fan_speed
        the_ac = CenturalAC()
        self.fee_rate: Fraction = the_ac.fee_rate[fan_speed]
        self.fee_sum: Fraction = Fraction()
        self.submit_time: float = time.time()  # 服务请求时间
        self.s_time: float = -1  # 服务开始时间
        self.e_time: float = -1  # 服务结束时间
        self.s_temp: Fraction = Fraction(-1)  # 服务开始温度
        self.e_temp: Fraction = Fraction(-1)  # 服务结束温度

    def update(self):
        """
        update room temprature of AC
        update fee_sum of service item and the AC
        according to feerate
        返回值与update_room_temp一致
        """

        self.fee_sum += self.fee_rate
        the_ac = CenturalAC()
        the_ac.fee_increase(self.room_id, self.fee_rate)
        ret = the_ac.update_room_temp(self.room_id)
        return ret

    def start_service(self):
        """
            set parameter of the AC and serv_item
        """
        self.s_time = time.time()
        # getstat实际index应为2
        the_ac = CenturalAC()
        the_ac.set_fan_speed(self.room_id, self.fan_speed)
        the_ac.set_room_mode(self.room_id, 'run')
        self.s_temp = the_ac.get_room_temp(self.room_id)

    def end_service(self):
        """
            1. get end time&temp then make a RD on deletion
            2. set parameter of the AC
        """
        if self.s_time == -1:  # 未开始服务，不生成详单
            return
        the_ac = CenturalAC()
        self.e_time = time.time()
        self.e_temp = the_ac.get_room_temp(self.room_id)
        the_ac.set_fan_speed(self.room_id, 0)
        the_rdm = RDManager([])
        the_rdm.create_rd(
            room_id=self.room_id, fan_speed=self.fan_speed,
            fee_rate=self.fee_rate, fee_sum=self.fee_sum,
            submit_time=self.submit_time,
            s_time=self.s_time, e_time=self.e_time,  # 服务结束
            s_temp=self.s_temp, e_temp=self.e_temp  # 服务结束
        )


class Queue(object):
    def __init__(self) -> None:
        self.queue: 'list[ServiceItem]' = []

    def insert_item(self, item: ServiceItem):
        """
        insert item
        """
        self.queue.append(item)

    def get_item(self, room_id: int):
        for idx, i in enumerate(self.queue):
            if i.room_id == room_id:
                return i
        return None

    def pop_item(self, room_id: int):
        """
        return None if no corresponding item
        """
        for idx, i in enumerate(self.queue):
            if i.room_id == room_id:
                return self.queue.pop(idx)
        return None


class ServQueue(Queue):
    """
    ServiceQueue = Queue + modify()
    """

    def __init__(self) -> None:
        super().__init__()

    def modify(self):
        """
        call update() of all Servitem in the list
        达到目标温度的房间列表为end_list
        """
        end_list = []
        for i in self.queue:
            if not i.update():
                end_list.append(i.room_id)
        return end_list


class Scheduler(object, metaclass=Singleton):
    def __init__(self, maxitem: int = 3, maxtime: int = 120, service_interval: float = 60) -> None:
        """
        maxtime: s
        service interval: 多长时间更新一次参数 单位:s
        """
        if maxitem <= 0 or maxtime <= 0:
            raise ValueError('Scheduler: Non-positive init parameter')
        self._max_item = maxitem
        self._max_time = maxtime
        self._service_interval = service_interval
        self._wait_queue = Queue()
        self._serv_queue = ServQueue()
        self.pause_list: 'list[int, int]' = []  # rid, fanspeed 需要保持温度的
        self.pause_lock = threading.Lock()
        self.service = threading.Thread(target=Scheduler.service_func, args=(self,))
        self.service.start()

    def submit_task(self, room_id: int, fan_speed: int):
        """
        提交任务、改变AC状态
        """
        self.end_task(room_id, False)  # 结束可能存在的任务
        the_ac = CenturalAC()
        the_ac.set_room_mode(room_id, 'wait')
        item = ServiceItem(room_id, fan_speed)
        self._wait_queue.insert_item(item)

    def end_task(self, room_id: int, if_kill: bool):
        """
        if_kill： 杀死pause中的任务
        """
        if if_kill:
            with self.pause_lock:
                for i in self.pause_list:
                    if i[0] == room_id:
                        self.pause_list.remove(i)
        r1 = self._wait_queue.pop_item(room_id)
        r2 = self._serv_queue.pop_item(room_id)
        # if r1 is not None:
        # r1.end_service()
        if r2 is not None:
            r2.end_service()

    def get_fan_speed(self, room_id: int):
        item = self._serv_queue.get_item(room_id)
        if item is not None:
            return item.fan_speed
        return 0

    def get_fee_sum(self, room_id: int):
        """
        get fee_sum of this service process
        -1 if not serving
        被user facade调用
        """
        item = self._serv_queue.get_item(room_id)
        if item is not None:
            return item.fee_sum
        return -1

    def get_serv_duration(self, room_id: int):
        """
        返回服务持续时长(秒)
        若不在服务则返回-1
        """
        item = self._serv_queue.get_item(room_id)
        if item is not None:
            if item.e_time == -1:
                return time.time() - item.s_time
            else:
                return item.e_time - item.s_time
        return -1

    def show_queue(self):
        the_ac = CenturalAC()
        if len(self._serv_queue.queue):
            print('serve_queue:')
        for i in self._serv_queue.queue:
            print('room id:', i.room_id, '\tfan_speed:', i.fan_speed, '\t serve_secs:', time.time() - i.s_time)
            print('room temp: {} / target temp: {}'.format(float(the_ac.get_room_temp(i.room_id)),
                                                           the_ac.get_target_temp(i.room_id)))
            print('fee sum:', float(the_ac.get_fee_sum(i.room_id)))
        if len(self._wait_queue.queue):
            print('wait_queue:')
        for i in self._wait_queue.queue:
            print('room id:', i.room_id, '\tfan_speed:', i.fan_speed, '\twait_secs:', time.time() - i.submit_time)
            print('room temp: {} / target temp: {}'.format(float(the_ac.get_room_temp(i.room_id)),
                                                           the_ac.get_target_temp(i.room_id)))
            print('fee sum:', float(the_ac.get_fee_sum(i.room_id)))

    def _wait2serv(self, room_id: int):
        """
        将对象从等待队列改至服务队列
        退出等待队列
        赋值服务开始相关变量
        设置对应空调模式
        插入服务队列
        """
        item = self._wait_queue.pop_item(room_id)
        if item is not None:
            item.start_service()
        else:
            raise ValueError('Scheduler: invalid room_id in wait2serv')
        self._serv_queue.insert_item(item)

    def _serv2wait(self, room_id: int):
        """
        将对象从服务队列改至等待队列
            1. 对象退出队列
            2. 对象结束服务
            3. 改变状态
            4. 创建新对象
            5. 插入等待队列
        """
        item = self._serv_queue.pop_item(room_id)
        if item is not None:
            item.end_service()
            the_ac = CenturalAC()
            the_ac.set_room_mode(room_id, 'wait')
        else:
            raise ValueError('Scheduler: invalid room_id in wait2serv')
        new_item = ServiceItem(item.room_id, item.fan_speed)
        self._wait_queue.insert_item(new_item)

    def service_func(self):
        """
            function to assure all items being served is legal
            and modify all items in service_queue
        """
        count = 1
        scan_mod: int = 10
        # modify次数为扫描次数的1/n
        the_ac = CenturalAC()
        the_ac.is_working.wait()  # 等待系统开启信号量
        while the_ac.is_working.is_set():
            self._schedule()
            print('loop')
            if count == scan_mod:
                count = 1
                # 将end_list内所有房号对应内容req release
                end_list = self._serv_queue.modify()
                self.show_queue()
                # 获得风速后poweroff并列入监视名单, 若监视名单室内温度不同于目标温度, 则将其启动并移出名单
                for rid in end_list:
                    print('room {} paused'.format(rid))
                    fs = self.get_fan_speed(rid)
                    the_ac.poweroff_ac(rid, False)
                    with self.pause_lock:
                        self.pause_list.append((rid, fs))
                remove_list = []
                if len(remove_list):
                    print('remove from pause:', remove_list)
                with self.pause_lock:
                    for rid, fs in self.pause_list:
                        room_temp = the_ac.get_room_temp(rid)
                        targ_temp = the_ac.get_target_temp(rid)
                        print(rid, ':', float(room_temp), '/', targ_temp)
                        if not room_temp == 0:
                            if (the_ac.cool_warm and room_temp > targ_temp) or \
                                    (not the_ac.cool_warm and room_temp < targ_temp):
                                print(float(room_temp), '/', targ_temp)
                                self.submit_task(rid, fs)
                                remove_list.append((rid, fs))
                for i in remove_list:
                    with self.pause_lock:
                        self.pause_list.remove(i)
            count += 1
            time.sleep(self._service_interval / scan_mod)

    def _schedule(self):
        # sort all items by fan_speed in descend order
        self._wait_queue.queue.sort(key=lambda i: (i.fan_speed, time.time() - i.submit_time), reverse=True)
        self._serv_queue.queue.sort(key=lambda i: (-i.fan_speed, time.time() - i.s_time), reverse=True)

        # fill up
        while len(self._serv_queue.queue) < self._max_item \
                and len(self._wait_queue.queue) != 0:
            self._wait2serv(self._wait_queue.queue[0].room_id)
        # 无需替换, 则返回
        if len(self._serv_queue.queue) + len(self._wait_queue.queue) \
                <= self._max_item:
            return

        # 服务队列：风速-等待时长优先
        # 等待队列: 风速越小-服务时长越长越优先被换出

        schlist: 'dict[int, int]' = {}  # wait room id : serv room id
        for s_item in self._serv_queue.queue:
            for w_item in self._wait_queue.queue:
                if w_item.room_id not in schlist.keys() and \
                        s_item.room_id not in [i[1] for i in schlist.items()]:
                    if w_item.fan_speed > s_item.fan_speed:
                        schlist[w_item.room_id] = s_item.room_id
                    if w_item.fan_speed == s_item.fan_speed and \
                            time.time() - w_item.submit_time >= self._max_time:
                        schlist[w_item.room_id] = s_item.room_id
        if len(schlist):
            print('rooms to exchange: (waiting, serving)', schlist)
        for w, s in schlist.items():
            self._serv2wait(s)
            self._wait2serv(w)
        if len(schlist.items()) != 0:
            self._wait_queue.queue.sort(key=lambda i: (i.fan_speed, time.time() - i.submit_time), reverse=True)
            self._serv_queue.queue.sort(key=lambda i: (-i.fan_speed, time.time() - i.s_time), reverse=True)


class DataBase(object, metaclass=Singleton):
    def __init__(self):
        """
            创建表格
        """
        self.count_temp: int = 0
        self.count_detail: int = 0
        self.dblock = threading.Lock()
        self.dbname = r'tables.db'
        if os.path.exists(self.dbname):
            os.remove(self.dbname)
        with sqlite3.connect(self.dbname) as db:
            with self.dblock:
                cur = db.cursor()
                command = 'create table temp_alter(count int primary key, roomid int, timestamp real not null);'
                results = cur.execute(command)
                # 房号 费率 风扇速度 提交时间 服务开始时间 服务结束时间 服务开始、结束温度
                command = 'create table details( \
                    count int primary key, room_id int, charge_fee real, fan_speed int, \
                    submit_time real, start_time real, end_time real, \
                    start_temp real, end_temp real, fee_sum real);'
                results = cur.execute(command)
                db.commit()
                cur.close()

    def _query(self, cmd: str):
        """
        execute an command, commit, and return its result
        """
        with sqlite3.connect(self.dbname) as db:
            with self.dblock:
                cur = db.cursor()
                results = []
                result = cur.execute(cmd)
                for line in result:
                    results.append(line)
                db.commit()
                cur.close()
                return results

    def show_tab(self):
        command = "select * from sqlite_master where type = 'table';"
        results = self._query(command)
        for line in results:
            print(line)

    def insert_temp_alter(self, room_id: int):
        self.count_temp += 1
        timestamp = time.time()
        command = "insert into temp_alter (count, roomid, timestamp) VALUES (" \
                  + str(self.count_temp) + ', ' + str(room_id) + ', ' + str(timestamp) + ");"
        results = self._query(command)
        print('insert temp alter successfully')

    def query_temp_alter(self, time_s: float = 0, time_e: float = time.time()):
        # command = 'select * from temp_alter where timestamp Between ' + str(time_s) + ' and ' + str(time_e)
        command = 'select * from temp_alter'
        results = self._query(command)
        ret = []
        print('temp alter results:', results)
        for result in results:
            ret.append((int(result[1]), float(result[2])))
        return ret

    def insert_detail(self, item: RD):
        self.count_detail += 1
        command = "insert into details (count, room_id, charge_fee, fan_speed, \
            submit_time, start_time, end_time, start_temp, end_temp, fee_sum) VALUES ("
        command += str(self.count_detail) + ', ' + str(item.room_id) + ', ' + str(item.fee_rate) + ', '
        command += str(item.fan_speed) + ', ' + str(item.submit_time) + ', '
        command += str(item.s_time) + ', ' + str(item.e_time) + ', '
        command += str(item.s_temp) + ', ' + str(item.e_temp) + ', ' + str(item.fee_sum) + ");"
        self._query(command)

    def query_details(self) -> 'list[RD]':
        """
        details query in terms of str
        """
        command = 'select * from details'
        results = self._query(command)
        ret = []
        for result in results:
            item = RD(int(result[1]), float(result[2]), int(result[3]), float(result[4]), float(result[5]),
                      float(result[6]), float(result[7]), float(result[8]), float(result[9]))
            ret.append(item)
        return ret


class UserFacade(object, metaclass=Singleton):

    def __init__(self, temp_lower: float, temp_upper: float, room_list: 'list[int]') -> None:
        ac = CenturalAC()
        if not ac.is_working.is_set():
            print("User Facade should be created after the creation of Centural AC")
            exit(1)
        self.temp_min = temp_lower
        self.temp_max = temp_upper
        self.roomlist = room_list
        self.mode_str: 'list[str]' = ['stop', 'run', 'wait', 'pause']

    def power_on(self, room_id: int):
        """
        房间号合法、状态合法后开机
        """
        if room_id not in self.roomlist:
            return False
        the_ac = CenturalAC()
        if the_ac.get_mode(room_id) != 'stop':
            return False
        the_ac.poweron_ac(room_id)
        return True

    def power_off(self, room_id: int):
        """
        房间号合法、状态合法后关机
        """
        if room_id not in self.roomlist:
            return False
        the_ac = CenturalAC()
        if the_ac.get_mode(room_id) == 'stop':
            return False
        the_ac.poweroff_ac(room_id, power_off=True)
        return True

    def request_state(self, room_id: int):
        """
            房间号合法后返回
        """
        if room_id not in self.roomlist:
            return None
        the_ac = CenturalAC()
        the_sch = Scheduler()
        return self.mode_str.index(the_ac.get_mode(room_id)), float(the_ac.get_room_temp(room_id)), \
               float(the_sch.get_fee_sum(room_id)), float(the_ac.get_fee_sum(room_id))

    def change_target_temp(self, room_id: int, temp: float):
        """
            房间号合法、温度在范围内后改动温度并且增加数据库记录
        """
        if room_id not in self.roomlist:  # 房间号不合法
            return False
        if temp < self.temp_min or temp > self.temp_max:  # 温度不在范围内
            return False
        the_ac = CenturalAC()
        t = the_ac.get_target_temp(room_id)
        for i in the_ac.AC_list.keys():
            print(i, 'target: ', the_ac.AC_list[i].target_temp)
        if t != temp:
            print('successfully', t, temp)
            the_db = DataBase()
            the_ac.set_target_temp(room_id, temp)
            the_db.insert_temp_alter(room_id)
        return True

    def change_room_temp(self, room_id: int, temp: float):
        """
            房间号合法后改动室内温度
        """
        if room_id not in self.roomlist:  # 房间号不合法
            return False
        the_ac = CenturalAC()
        the_ac.set_room_temp(room_id, temp)
        return True

    def change_fan_speed(self, room_id: int, fan_speed: int):
        """
            房间号合法、风速合法后提交申请
            风速暂时有4挡: 0 1 2 3
            但用户不能手动将风速调为0
        """
        if room_id not in self.roomlist:
            return False
        if fan_speed not in range(1, 4):
            return False
        the_ac = CenturalAC()
        fs = the_ac.get_fan_speed(room_id)
        if fs != fan_speed:
            the_ac.change_fan_speed(room_id, fan_speed)
        return True


class ReceptionFacade(object, metaclass=Singleton):
    def __init__(self, room_list: 'list[int]') -> None:
        """
        true 代表未被使用, false 代表正被使用
        """
        ac = CenturalAC()
        if not ac.is_working.is_set():
            print("Reception Facade should be created after the creation of Centural AC")
            exit(1)
        self.room_stat: 'dict[int, bool]' = {i: True for i in room_list}
        self.checkin_time = dict()

    def checkin(self):
        """
        返回一个房间号，代表分配房间号
        返回-1代表无空房
        """
        the_ac = CenturalAC()
        for i in the_ac.room_list:
            if self.room_stat[i]:
                self.room_stat[i] = False
                self.checkin_time[i] = time.time()
                return i
        return -1

    def checkout(self, room_id: int):
        """
        更新是否使用列表
        重置该房信息, 存放数据
        非法房间号: 返回False
        """
        the_ac = CenturalAC()
        the_rdm = RDManager([])
        if room_id not in the_ac.room_list or self.room_stat[room_id]:
            return False
        self.room_stat[room_id] = True
        self.checkin_time.pop(room_id)
        the_ac.reset_ac(room_id)
        the_rdm.archive_rd(room_id)
        gc.collect()  # garbage collection
        return True

    def get_bill(self, room_id: int):
        """
        获取账单, 时间: checkin时间-> checkout时间
        """
        the_ac = CenturalAC()
        if room_id not in the_ac.room_list or self.room_stat[room_id]:
            return False
        fee_sum = Fraction()
        rdm = RDManager([])
        for rd in rdm.get_mem_rd(room_id):
            fee_sum += rd.fee_sum
        in_time: float = self.checkin_time[room_id]
        out_time: float = time.time()
        return float(fee_sum), in_time, out_time

    def get_rd(self, room_id: int):
        """
        获取详单
        json 格式
        """
        the_ac = CenturalAC()

        if room_id not in the_ac.room_list or self.room_stat[room_id]:
            return False

        the_rdm = RDManager([])
        rd_list = the_rdm.RD_list[room_id]
        ret = []
        for rd in rd_list:
            ret += rd.to_list()
        leng = len(ret)
        ret = json.dumps(ret)
        return ret


def to_timestr(timestamp, get_date=False):
    time_local = time.localtime(timestamp)
    timestr = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
    if get_date:
        timestr = re.sub(r' \d+:\d+:\d+', '', timestr)
    return timestr


def manager_get_report(report_type=0):
    the_db = DataBase()
    the_rdm = RDManager([])
    deg_change_list = the_db.query_temp_alter()
    rd_list = the_rdm.get_all_rd()

    data_list = {rid: [0, 0, 0, Fraction()] for rid in set(rd.room_id for rd in rd_list). \
        union(set(i[0] for i in deg_change_list))}
    # list: 0--详单条数 1--调温次数 2--request time 3--fee_sum 
    for rd in rd_list:
        data_list[rd.room_id][0] += 1  # 详单条数
        data_list[rd.room_id][2] += (rd.e_time - rd.s_time) / 60
        data_list[rd.room_id][3] += rd.fee_sum
    for rid, _ in deg_change_list:
        data_list[rid][1] += 1

    rd_num = [data_list[rid][0] for rid in data_list.keys()]
    tmp_change = [data_list[rid][1] for rid in data_list.keys()]
    req_time = [data_list[rid][2] for rid in data_list.keys()]
    fee_sum = [float(data_list[rid][3]) for rid in data_list.keys()]

    if_tmp = 0
    if sum(tmp_change):
        print('changed:', tmp_change)
        if_tmp = 1
    if report_type == 0 and len(rd_list):  # 饼图
        plt.figure(dpi=200)
        plt.subplots_adjust(hspace=1, wspace=0.3)
        fignum = 3 + if_tmp
        plt.subplot(fignum, 1, 1), plt.pie(x=rd_num, labels=data_list.keys(), autopct='%.2f%%',
                                           textprops={'fontsize': 4})
        plt.title('RD number', fontsize=6)
        plt.subplot(fignum, 1, 2), plt.pie(x=req_time, labels=data_list.keys(), autopct='%.2f%%',
                                           textprops={'fontsize': 4})
        plt.title('service time', fontsize=6)
        plt.subplot(fignum, 1, 3), plt.pie(x=fee_sum, labels=data_list.keys(), autopct='%.2f%%',
                                           textprops={'fontsize': 4})
        plt.title('fee sum', fontsize=6)
        if if_tmp:
            plt.subplot(4, 1, 4), plt.pie(x=tmp_change, labels=data_list.keys(), autopct='%.2f%%',
                                          textprops={'fontsize': 4})
            plt.title('temprature change', fontsize=6)
        plt.suptitle('{} AC report'.format(to_timestr(time.time(), True)), fontsize=8)
    elif report_type == 1 and len(rd_list):  # 折线图
        plt.figure(figsize=(2, 5), dpi=100)
        fignum = 3 + if_tmp
        plt.subplots_adjust(hspace=1, wspace=0.3)
        plt.subplot(fignum, 1, 1), plt.plot(data_list.keys(), rd_num, '-.p'), plt.tick_params(labelsize=6)
        plt.title('RD number', fontsize=10)
        plt.subplot(fignum, 1, 2), plt.plot(data_list.keys(), req_time, '-.p'), plt.tick_params(labelsize=6)
        plt.title('service time', fontsize=10)
        plt.subplot(fignum, 1, 3), plt.plot(data_list.keys(), fee_sum, '-.p'), plt.tick_params(labelsize=6)
        plt.title('fee sum', fontsize=10)
        if if_tmp:
            plt.subplot(4, 1, 4), plt.plot(data_list.keys(), tmp_change, '-.p'), plt.tick_params(labelsize=6)
            plt.title('temprature change', fontsize=10)
        plt.suptitle('{} AC report'.format(to_timestr(time.time(), True)), fontsize=8)
    import re
    addr = re.sub('/[^/]+$', '/report.jpg', os.path.abspath(__file__))
    plt.savefig(addr)
    print('report generated')
    return addr
